package cc.shacocloud.mirage.core;

import cc.shacocloud.mirage.bean.bind.Component;
import cc.shacocloud.mirage.core.event.ContextFinishStartEvent;
import cc.shacocloud.mirage.utils.Utils;
import cc.shacocloud.mirage.utils.annotation.AnnotatedElementUtils;
import cc.shacocloud.mirage.utils.annotation.AnnotationAttributes;
import cc.shacocloud.mirage.utils.charSequence.StrUtil;
import cc.shacocloud.mirage.utils.collection.ArrayUtil;
import io.vertx.core.*;
import io.vertx.core.impl.Deployment;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 部署基于 {@link DeployVerticle} 注释，{@link ApplicationContextVerticleFactory}构造的 的 Verticle
 *
 * @author shaco
 * @see DeployVerticle
 * @see ApplicationContextVerticleFactory
 */
@Slf4j
@Component
public class DeployVerticleBeanHandler implements ApplicationListener<ContextFinishStartEvent> {
    
    private final Vertx vertx;
    private final ApplicationContextVerticleFactory verticleFactory;
    
    public DeployVerticleBeanHandler(Vertx vertx,
                                     ApplicationContextVerticleFactory verticleFactory) {
        this.vertx = vertx;
        this.verticleFactory = verticleFactory;
    }
    
    @SuppressWarnings("rawtypes")
    @Override
    public @NotNull Future<Void> onApplicationEvent(@NotNull ContextFinishStartEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        String[] beanNames = applicationContext.getBeanNamesForAnnotation(DeployVerticle.class);
        if (ArrayUtil.isEmpty(beanNames)) return Future.succeededFuture();
        
        List<Future> futures = new ArrayList<>();
        
        for (String beanName : beanNames) {
            // 解析部署实例的描述信息
            Object bean = applicationContext.getBean(beanName);
            DeployVerticleDescriptor deployVerticleDescriptor = parseVerticleBean(beanName, bean);
            if (Objects.isNull(deployVerticleDescriptor)) continue;
            
            // 部署
            Future<String> deployFuture = deploy(deployVerticleDescriptor)
                    .onFailure(cause -> {
                        if (log.isErrorEnabled()) {
                            log.error("部署Bean名称为 {} 的 Verticle 时发生例外！", beanName, cause);
                        }
                    });
            
            futures.add(deployFuture);
        }
        
        Promise<Void> promise = Promise.promise();
        CompositeFuture.join(futures)
                .onComplete(ar -> {
                    if (ar.succeeded()) {
                        promise.complete();
                    } else {
                        promise.fail("站点部署部分发生例外！");
                    }
                });
        
        return promise.future();
    }
    
    /**
     * 部署站点
     *
     * @return 返回部署id {@link Deployment#deploymentID()}
     */
    protected Future<String> deploy(@NotNull DeployVerticleDescriptor verticleDescriptor) {
        String verticleName = verticleDescriptor.getVerticleName();
        if (log.isInfoEnabled()) {
            log.info(String.format("部署站点[%s]，实例数量：%s，Worker Pool：%s，是否高可用：%s",
                    verticleName,
                    verticleDescriptor.getInstances(),
                    StrUtil.isEmpty(verticleDescriptor.getWorkerPoolName()) ? verticleDescriptor.getWorkerPoolSize() : "使用全局共享",
                    verticleDescriptor.isHa()));
        }
        
        // 部署站点 使用同步
        return vertx.deployVerticle(verticleName, verticleDescriptor);
    }
    
    /**
     * 解析部署的bean，如果不合法则返回 null
     */
    @Nullable
    protected DeployVerticleDescriptor parseVerticleBean(String beanName, Object bean) {
        // 非 Verticle 实现对象 返回 null
        if (!(bean instanceof Verticle)) {
            return null;
        }
        
        Class<?> targetClass = bean.getClass();
        AnnotationAttributes annotationAttributes = AnnotatedElementUtils.getAnnotationAttributes(targetClass, DeployVerticle.class);
        if (Objects.isNull(annotationAttributes)) return null;
        
        
        DeployVerticleDescriptor descriptor = new DeployVerticleDescriptor();
        descriptor.setVerticleName(verticleFactory.verticleName(beanName));
        descriptor.setInstances(annotationAttributes.getNumber("instances"));
        descriptor.setHa(annotationAttributes.getBoolean("ha"));
        descriptor.setWorkerPoolName(Utils.emptyOrDefault(annotationAttributes.getString("workerPoolName"), () -> null));
        descriptor.setWorkerPoolSize(annotationAttributes.getNumber("workerPoolSize"));
        descriptor.setMaxWorkerExecuteTime(annotationAttributes.getNumber("maxWorkerExecuteTime"));
        descriptor.setMaxWorkerExecuteTimeUnit(TimeUnit.SECONDS);
        return descriptor;
    }
    
}
